{-
 - phc -- the open source PHP compiler
 - See doc/license/README.license for licensing information
 -
 - The phc grammar in .tea format. 
 -
 - This is the authoritative grammar for phc. 
 -}

{-
	Configuration
-}

prefix "AST";
external class "Object";
use list "List";
use string "String";
use namespace "AST";

{-
   Top-level structure	
-}

php_script ::= statement* ;

statement ::=
     class_def | interface_def | method  
   | if | while | do | for | foreach 
   | switch | break | continue | return
   | static_declaration | global
   | declare | try | throw | eval_expr | nop 
	| label | goto | branch ;

class_def ::= 
   class_mod CLASS_NAME extends:CLASS_NAME? implements:INTERFACE_NAME* member* ;
class_mod ::= "abstract"? "final"? ;

interface_def ::= INTERFACE_NAME extends:INTERFACE_NAME* member* ;

member ::= method | attribute ;

method ::= signature statement*? ;
signature ::= method_mod is_ref:"&"? METHOD_NAME formal_parameter* ;
method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ;
formal_parameter ::= type is_ref:"&"? VARIABLE_NAME expr? ;
type ::= CLASS_NAME? ;

attribute ::= attr_mod VARIABLE_NAME expr? ;
attr_mod ::= "public"? "protected"? "private"? "static"? "const"?  ;

{-
   Statements
-}

if ::= expr iftrue:statement* iffalse:statement* ;
while ::= expr statement* ;
do ::= statement* expr ;
for ::= init:expr? cond:expr? incr:expr? statement* ;
foreach ::= expr key:variable? is_ref:"&"? val:variable statement* ;

switch ::= expr switch_case* ;
switch_case ::= expr? statement* ;
break ::= expr? ;
continue ::= expr? ;
return ::= expr? ;

static_declaration ::= VARIABLE_NAME expr? ;
global ::= variable_name ;

declare ::= directive+ statement* ;
directive ::= DIRECTIVE_NAME expr ;

try ::= statement* catches:catch* ;
catch ::= CLASS_NAME VARIABLE_NAME statement* ;
throw ::= expr ;

eval_expr ::= expr ;

nop ::= ;

{-
   HIR constructs (without a PHP equivalent)
-}

branch ::= expr iftrue:LABEL_NAME iffalse:LABEL_NAME;
goto ::= LABEL_NAME ;
label ::= LABEL_NAME ;

{-
   Expressions
-}

expr ::=
     assignment | op_assignment | list_assignment 
   | cast | unary_op | bin_op | conditional_expr
   | ignore_errors | constant | instanceof
   | variable | pre_op | post_op | array
   | method_invocation | new 
	| literal ;
	
literal ::= INT<long> | REAL<double> | STRING<String*> | BOOL<bool> | NULL<> ;
   
assignment ::= variable is_ref:"&"? expr ;
op_assignment ::= variable OP expr ;

list_assignment ::= list_element?* expr ;
list_element ::= variable | nested_list_elements ;
nested_list_elements ::= list_element?* ;

cast ::= CAST expr ;
unary_op ::= OP expr ;
bin_op ::= left:expr OP right:expr ;

conditional_expr ::= cond:expr iftrue:expr iffalse:expr ;
ignore_errors ::= expr ;

constant ::= CLASS_NAME? CONSTANT_NAME ;

instanceof ::= expr class_name ;

variable ::= target? variable_name array_indices:expr?* ;
variable_name ::= VARIABLE_NAME | reflection ;
reflection ::= expr ;

target ::= expr | CLASS_NAME ;

pre_op ::= OP variable ;
post_op ::= variable OP ;

array ::= array_elem* ;
array_elem ::= key:expr? is_ref:"&"? val:expr ;

method_invocation ::= target? method_name actual_parameter* ;
method_name ::= METHOD_NAME | reflection ;

actual_parameter ::= is_ref:"&"? expr ;

new ::= class_name actual_parameter* ;
class_name ::= CLASS_NAME | reflection ;

{-
 - Additional structure 
 -}

commented_node ::= 
	  member | statement | interface_def | class_def | switch_case | catch 
	;

identifier ::=
	  INTERFACE_NAME | CLASS_NAME | METHOD_NAME | VARIABLE_NAME 
	| DIRECTIVE_NAME | CAST | OP | CONSTANT_NAME
	| LABEL_NAME
	;  

{-
 - Extra attributes and methods (mixin code)
 -}

#include <iostream>
#include <sstream>
#include <iomanip>
#include "lib/error.h"
#include "lib/Object.h"
#include "lib/List.h"
#include "lib/String.h"
#include "lib/Boolean.h"
#include "lib/Integer.h"
#include "lib/AttrMap.h"

class AST_node : Object 
{
public:
	AttrMap* attrs;

	// Return the line number of the node (or 0 if unknown)
	int get_line_number()
	{
		Integer* i = dynamic_cast<Integer*>(attrs->get("phc.line_number"));
		if(i != NULL)
			return i->value();
		else
			return 0;
	}

	// Return the filename of the node (or NULL if unknown)
	String* get_filename()
	{
		return dynamic_cast<String*>(attrs->get("phc.filename"));
	}

	AST_node()
	{
		// Constructor gets called because all classes inherit from
		// AST_node virtually; also, because maketea knows AST_node is
		// abstract, it won't add a constructor itself
		attrs = new AttrMap();
	}

	void clone_mixin_from(AST_node* in)
	{
		attrs = in->attrs->clone();
	}

	void assert_mixin_valid()
	{
		assert(attrs != NULL);

		AttrMap::const_iterator i;
		for(i = attrs->begin(); i != attrs->end(); i++)
		{
			if ((*i).first != "phc.line_number"
				&& (*i).first != "phc.filename")
			{
				assert((*i).second != NULL);
			}
		}
	}

	bool is_mixin_equal(AST_node* in)
	{
		// Compare line number and filename
		// (We can't compare the entire attrs map because Object cannot
		// necessarily be compared for equality)

		if(get_line_number() != in->get_line_number())
			return false;

		if(get_filename() == NULL)
		{
			if(in->get_filename() != NULL)
				return false;
		}
		else
		{
			if(*get_filename() != *in->get_filename())
				return false;
		}

		return true;
	}
};

class AST_commented_node 
{
public:
	AST_commented_node()
	{
		attrs->set("phc.comments", new List<String*>);
	}

	// Return the comments associated with the node
	List<String*>* get_comments()
	{
		List<String*>* comments = dynamic_cast<List<String*>*>(attrs->get("phc.comments"));
		assert(comments);
		return comments;
	}
};

class AST_signature 
{
public:
	AST_signature(const char* name) 
	{
		this->method_mod = AST_method_mod::new_PUBLIC();
		this->is_ref = false;
		this->method_name = new Token_method_name(new String(name));
		this->formal_parameters = new List<AST_formal_parameter*>;
	}
};

class AST_method_mod 
{
public:
	AST_method_mod()
	{
		is_public = false;
		is_protected = false;
		is_private = false;
		is_static = false;
		is_abstract = false;
		is_final = false;
	}

	AST_method_mod(AST_method_mod* a, AST_method_mod* b) 
	{
		this->is_public 		= a->is_public		|| b->is_public;
		this->is_protected	= a->is_protected	|| b->is_protected;
		this->is_private		= a->is_private	|| b->is_private;
		this->is_static		= a->is_static		|| b->is_static;
		this->is_abstract		= a->is_abstract	|| b->is_abstract;
		this->is_final			= a->is_final		|| b->is_final;
	}

	static AST_method_mod* new_PUBLIC() 
	{
		return new AST_method_mod(true, false, false, false, false, false);		
	}

	static AST_method_mod* new_PROTECTED() 
	{ 
		return new AST_method_mod(false, true, false, false, false, false);		
	}

	static AST_method_mod* new_PRIVATE() 
	{ 
		return new AST_method_mod(false, false, true, false, false, false);		
	}

	static AST_method_mod* new_STATIC() 
	{ 
		return new AST_method_mod(false, false, false, true, false, false);		
	}

	static AST_method_mod* new_ABSTRACT() 
	{ 
		return new AST_method_mod(false, false, false, false, true, false);		
	}

	static AST_method_mod* new_FINAL() 
	{ 
		return new AST_method_mod(false, false, false, false, false, true);		
	}
};

class AST_class_def
{
public:
	AST_class_def(AST_class_mod* mod) 
	{
		this->class_mod = mod;
		this->class_name = NULL;
		this->extends = NULL;
		this->implements = new List<Token_interface_name*>;
		this->members = new List<AST_member*>;
	}

	AST_class_def(const char* name)
	{
		this->class_mod = new AST_class_mod(false, false);
		this->class_name = new Token_class_name(new String(name));
		this->extends = NULL;
		this->implements = new List<Token_interface_name*>;
		this->members = new List<AST_member*>;
	}

	void add_member(AST_member* member) 
	{
		this->members->push_back(member);
	}

	// Returns NULL if the method could not be found
	AST_method* get_method(const char* name)
	{
		List<AST_member*>::const_iterator i;
		for(i = members->begin(); i != members->end(); i++)
		{
			AST_method* method = dynamic_cast<AST_method*>(*i);
			if(method && *method->signature->method_name->value == name)
				return method;
		}

		return NULL;
	}
};

class AST_eval_expr
{
public:
	void _init()
	{
		assert (expr != NULL);
	}
};

class AST_variable
{
public:
	AST_variable(AST_variable_name* name) 
	{
		this->target = NULL;
		this->variable_name = name;
		this->array_indices = new List<AST_expr*>;
	}
};

class AST_method_invocation
{
public:
	AST_method_invocation(const char* name, AST_expr* arg) 
	{ 
		this->target = NULL;
		this->method_name = new Token_method_name(new String(name));
		this->actual_parameters = new List<AST_actual_parameter*>;
		this->actual_parameters->push_back(new AST_actual_parameter(false, arg));
	}

	AST_method_invocation(Token_method_name* name, AST_expr* arg) 
	{ 
		this->target = NULL;
		this->method_name = name; 
		this->actual_parameters = new List<AST_actual_parameter*>;
		this->actual_parameters->push_back(new AST_actual_parameter(false, arg));
	}
};

class AST_formal_parameter
{
public:
	AST_formal_parameter(AST_type* type, Token_variable_name* name) 
	{
		this->type = type;
		this->is_ref = false;
		this->variable_name = name;
		this->expr = NULL;
	}

	AST_formal_parameter(AST_type* type, bool is_ref, Token_variable_name* name) 
	{ 
		this->type = type;
		this->is_ref = is_ref;
		this->variable_name = name;
		this->expr = NULL;
	}
};

class AST_attr_mod
{
public:
	AST_attr_mod()
	{
		is_public = false;
		is_protected = false;
		is_private = false;
		is_static = false;
		is_const = false;
	}

	AST_attr_mod(AST_method_mod* mm) 
	{
		if(mm->is_final)
			phc_error("The final modifier is only allowed for methods", mm->get_filename(), mm->get_line_number());

		this->is_public = mm->is_public;
		this->is_protected = mm->is_protected;
		this->is_private = mm->is_private;
		this->is_static = mm->is_static;
		this->is_const = false;
	}

	static AST_attr_mod* new_PUBLIC() 
	{
		return new AST_attr_mod(true, false, false, false, false);
	}

	static AST_attr_mod* new_PROTECTED() 
	{ 
		return new AST_attr_mod(false, true, false, false, false);
	}

	static AST_attr_mod* new_PRIVATE() 
	{
		return new AST_attr_mod(false, false, true, false, false);
	}

	static AST_attr_mod* new_STATIC() 
	{
		return new AST_attr_mod(false, false, false, true, false);
	}
	
	static AST_attr_mod* new_CONST() 
	{
		return new AST_attr_mod(false, false, false, false, true);
	}
};

class AST_op_assignment
{
public:
   AST_op_assignment(AST_variable* variable, const char* op, AST_expr* expr)
   {
      this->variable = variable;
      this->op = new Token_op(new String(op));
      this->expr = expr;
   }
};

class AST_bin_op
{
public:
	AST_bin_op(AST_expr* left, AST_expr* right, const char* op) 
	{
		this->left = left;
		this->op = new Token_op(new String(op));
		this->right = right;
	}
};

class AST_post_op
{
public:
	AST_post_op(AST_variable* var, const char* op) 
	{
		this->variable = var;
		this->op = new Token_op(new String(op));
	}
};

class AST_pre_op
{
public:
	AST_pre_op(AST_variable* var, const char* op) 
	{
		this->variable = var;
		this->op = new Token_op(new String(op));
	}
};

class AST_unary_op
{
public:
	AST_unary_op(AST_expr* expr, const char* op) 
	{
		this->expr = expr;
		this->op = new Token_op(new String(op));
	}
};

class AST_if
{
public:
	AST_if(AST_expr* expr)
	{
		AST_if (expr, new List<AST_statement*> (), new List<AST_statement*>);
	}
};

class AST_cast
{
public:
	AST_cast(const char* cast, AST_expr* expr) 
	{
		this->cast = new Token_cast(new String(cast));
		this->expr = expr;
	}
};

class AST_identifier
{
public:
	virtual String* get_value_as_string() = 0;
};

class AST_literal
{
public:
	virtual String* get_value_as_string() = 0;
	virtual String* get_source_rep() = 0;
};

class Token_int
{
private:

	// Constructors can't call virtual functions, so we create a non-virtual to
	// do the work. This is then called by the virtual function, and is also
	// safely called from the constructor.
	String* _get_value_as_string()
	{
		std::ostringstream os;
		os << value;
		return new String(os.str());
	}

public:
	virtual String* get_value_as_string()
	{
		return _get_value_as_string ();
	}

	Token_int (int v)
	{
		value = v;
		source_rep = _get_value_as_string ();
	}
};

class Token_real
{
private:

	// See comment in Token_int::_get_value_as_string ()
	String* _get_value_as_string()
	{
		std::ostringstream os;
		// setprecision(20) outputs as many digits as required, with
		// a maximum of 20
		os << setprecision(20) << value;
		// unfortunately, that means that if no digits are required at
		// all (after the decimal point), the decimal point is left out
		// completely; setting the "showpoint" flag fixes this, but then
		// the STL _always_ shows all 20 digits, which is not what we 
		// want either. Hence, we insert the ".0" manually if necessary:
		string str = os.str();
		if(str.find('.') >= str.size())
			str.append(".0");

		return new String(str);
	}

public:
	virtual String* get_value_as_string()
	{
		return _get_value_as_string ();
	}

	Token_real (double v)
	{
		value = v;
		source_rep = _get_value_as_string ();
	}
};

class Token_bool
{
private:
	// See comment in Token_int::_get_value_as_string ()
	String* _get_value_as_string()
	{
		if(value)
			return new String("True");
		else
			return new String("False");
	}

public:
	virtual String* get_value_as_string()
	{
		return _get_value_as_string ();
	}

	Token_bool (bool v)
	{
		value = v;
		source_rep = _get_value_as_string ();
	}
};

class Token_string
{
public:
	virtual String* get_value_as_string()
	{
		return value;
	}

	bool is_value_valid()
	{
		return value != NULL;
	}

	String* clone_value()
	{
		return value->clone();
	}

	Token_string (String* v)
	{
		value = v;
		source_rep = v;
	}
};

class Token_null
{
public:
	virtual String* get_value_as_string()
	{
		return new String("NULL");
	}
	Token_null ()
	{
		source_rep = new String ("NULL");
	}
};
